from common.recordlog import logs
from common.readyaml import get_testcase_yaml
import os
import jsonpath
import allure
import operator
from common.connection import ConnectMysql

class Assertions:
    """
    接口断言模式封装
    1.字符串包含
    2.结果相等断言
    3.结果不相等断言
    4.断言接口返回值里面的任意一个值
    5.数据库断言
    """

    def contains_assert(self,value,response,status_code):
        """
        字符串包含断言，断言预期结果的字符串是否包含在接口的实际返回结果当中
        """
        # 断言状态标识，0表示成功，其他代表失败
        flag = 0
        for assert_key,assert_value in value.items():
            if assert_key == 'status_code':
                if assert_value != status_code:
                    flag+=1
                    allure.attach(f'预期结果：{assert_value}\n实际结果：{status_code}','响应代码断言结果：失败',allure.attachment_type.TEXT)
                    logs.error('contains断言失败，接口返回码【%s】不等于【%s】'%(status_code,assert_value))
            else:
                resp_list = jsonpath.jsonpath(response,'$..%s' %assert_key)
                if isinstance(resp_list[0],str):
                    resp_list = ''.join(resp_list)
                if resp_list:
                    if assert_value in resp_list:
                        logs.info('字符串包含断言成功：预期结果为：【%s】,实际结果为：【%s】'%(assert_value,resp_list))
                    else:
                        flag+=1
                        allure.attach(f'预期结果：{assert_value}\n实际结果：{resp_list}','响应文本断言结果：失败',allure.attachment_type.TEXT)
                        logs.error('响应文本断言失败：预期结果为：【%s】,实际结果为：【%s】'%(assert_value,resp_list))
        return flag
    
    def equal_assert(self,value,response):
        """ 
        相等断言模式
        value: 预期结果，也就是yaml文件里面的validation关键字下的参数，必须为dict(字典)类型
        response: 接口的实际返回结果，必须为dict类型
        @return: flag标识，0表示测试通过，非0表示测试不通过
        """
        flag = 0
        res_lst = [] #列表(数组)
        if isinstance(value,dict) and isinstance(response,dict):
            for res in response:
                if list(value.keys())[0] != res:
                    res_lst.append(res)

            for rl in res_lst:
                del response[rl]

            # 通过判断实际结果的字典和预期结果的字典
            eq_assert = operator.eq(response,value)
            if eq_assert:
                logs.info(f'相等断言成功：接口的预期结果为:{str(value)},等于实际结果:{response}')
            else:
                flag = flag +1
                logs.error(f'相等断言失败：接口的预期结果为:{str(value)},不等于实际结果:{response}')
        else:
            raise TypeError('相等断言失败--类型错误，预期结果和接口的实际响应结果必须为字典类型！')
        return flag

    def not_equal_assert(self,value,response):
        """ 
        不相等断言模式
        value: 预期结果，也就是yaml文件里面的validation关键字下的参数，必须为dict(字典)类型
        response: 接口的实际返回结果，必须为dict类型
        @return: flag标识，0表示测试通过，非0表示测试不通过
        """
        flag = 0
        res_lst = [] #列表(数组)
        if isinstance(value,dict) and isinstance(response,dict):
            for res in response:
                if list(value.keys())[0] != res:
                    res_lst.append(res)

            for rl in res_lst:
                del response[rl]

            # 通过判断实际结果的字典和预期结果的字典
            eq_assert = operator.ne(response,value)
            if eq_assert:
                logs.info(f'不相等断言成功：接口的预期结果为:{str(value)},不等于实际结果:{response}')
            else:
                flag = flag +1
                logs.error(f'不相等断言失败：接口的预期结果为:{str(value)},等于实际结果:{response}')
        else:
            raise TypeError('不相等断言失败--类型错误，预期结果和接口的实际响应结果必须为字典类型！')
        return flag
    
    def assert_response_any(self, expected_results,actual_results):
        """
        断言接口响应信息中的body的任何属性值
        :param actual_results: 接口实际响应信息
        :param expected_results: 预期结果，在接口返回值的任意值
        :return: 返回标识,0表示测试通过，非0则测试失败
        """
        flag = 0
        try:
            exp_key = list(expected_results.keys())[0]
            if exp_key in actual_results:
                act_value = actual_results[exp_key]
                rv_assert = operator.eq(act_value, list(expected_results.values())[0])
                if rv_assert:
                    logs.info("响应结果任意值断言成功")
                else:
                    flag += 1
                    logs.error("响应结果任意值断言失败")
        except Exception as e:
            logs.error(e)
            raise
        return flag
    
    def assert_mysql(self,expected_sql):
        """
        数据库断言模式
        expected_sql: 预期结果，也就是yaml文件的sql语句
        @return: 返回flag标识，0表示测试通过，非0表示测试失败
        """
        flag = 0 
        conn = ConnectMysql()
        db_value = conn.query(expected_sql)
        if db_value is not None:
            logs.info('数据库断言成功')
        else:
            flag = flag + 1
            logs.error('数据库断言失败，请检查数据库是否存在该数据！')
        return flag


    def assert_result(self,expected,response,status_code):
        """
        断言模式，通过all_flag标记
        expected: 预期结果
        response: 实际结果，json格式
        status_code: 接口实际返回状态码
        """
        all_flag = 0
        # 0代表成功，其他代表失败
        try:
            for yq in expected:
                for key,value in yq.items():
                    if key== 'contains':
                        flag = self.contains_assert(value,response,status_code)
                        all_flag =all_flag + flag
                    elif key == 'eq':
                        flag = self.equal_assert(value,response)
                        all_flag = all_flag + flag
                    elif key == 'ne':
                        flag = self.not_equal_assert(value,response)
                        all_flag = all_flag + flag
                    elif key == 'rv':
                        flag = self.assert_response_any(value,response)
                        all_flag = all_flag + flag
                    elif key == 'db':
                        flag = self.assert_mysql(value)
                        all_flag = all_flag + flag
                    else:
                        logs.error("不支持此种断言方式")

            assert all_flag == 0
            logs.info('测试成功！')
        except Exception as e:
            logs.error('测试失败！')
            logs.error(f'异常信息：{e}')
            assert all_flag == 0

